
module video
(
    input  clk_50M,
    input  reset_n,               // reset.reset
    
    input   [24:0]    avs_s0_address,     //    s0.address
    input             avs_s0_read,        //      .read
    input             avs_s0_write,       //      .write
    output  [31:0]    avs_s0_readdata,    //      .readdata
    input   [31:0]    avs_s0_writedata,   //      .writedata
    input             device_req, //      .waitrequest
    output reg        device_ack, //      .waitrequest
    input   [3:0]     avs_s0_byteenable,    //      .readdata

    output reg        vga_mode,
    output reg        blockvga,
    output reg [15:0] vga_base_addr,

    output reg  [9:0]  cursor_posX,//0~1023
    output reg  [9:0]  cursor_posY,//0~1023

    output [15:0] read_cursor_data,
    input [9:0]   read_cursor_addr,//32 * 32
    input         read_cursor_clk,

  
    input         dma_sram_clk,
    output  reg   read_dma_req,
    output  reg   write_dma_req,
    input         read_dma_ack,
    input         write_dma_ack,
    output [9:0]  dma_len,
    output [24:0] dma_start_addr,
    output [15:0] dma_sram_rdata,
    input  [8:0]  dma_sram_address,
    input [15:0]  dma_sram_wdata,
    input         dma_sram_write_enable
    
);

//`ifdef isALTERA
//  altsource_probe	prob1 (
//      .probe ({
//        blockvga
//      }),
//      .source ()
//      );
//  defparam
//    prob1.enable_metastability = "NO",
//    prob1.instance_id = "P1",
//    prob1.probe_width = 16,
//    prob1.sld_auto_instance_index = "YES",
//    prob1.sld_instance_index = 0,
//    prob1.source_initial_value = " 0",
//    prob1.source_width = 1;
//`endif
//

  reg device_req_buff;
  reg memcpy_req;
  reg memset_req;
  reg [9:0] memcpy_startx;//0~1024px
  reg [15:0] memcpy_starty;//0~16384line
  reg [9:0] memcpy_width;//0~512px
  reg [15:0] memcpy_height;//0~16384line
  reg [31:0] memset_val;
  reg [31:0] memset_pattern;
  reg [9:0] memcpy_destx;//0~1024px
  reg [15:0] memcpy_desty;//0~16384line
  reg write_cursor_ena;
  reg [7:0] write_cursor_data;
  reg [9:0] write_cursor_addr;
  always@(posedge clk_50M or negedge reset_n) begin
    if(!reset_n) begin
      vga_mode <= 1;
      blockvga <= 0;
      vga_base_addr <= 0;
      write_cursor_ena <= 0;
      write_cursor_addr <= 0;
      write_cursor_data <= 0;
      device_req_buff <= 0;
      device_ack <= 0;
      memcpy_req <= 0;
      memset_req <= 0;
      memset_pattern <= 0;
    end else begin

      device_req_buff <= device_req;
    
      write_cursor_ena <= 0;
      write_cursor_addr <= avs_s0_address[11:2];
      write_cursor_data <= {avs_s0_writedata[15:13],avs_s0_writedata[10:8],avs_s0_writedata[4:3]};//565->332
    
      if(device_req_buff && !device_ack)begin
        device_ack <= 1;
      end
      
      if(device_req_buff && avs_s0_write)begin
        case(avs_s0_address[16:13])
        0: begin // 0x0000 dw
          vga_mode <= avs_s0_writedata[0];
        end
        1: begin // 0x0800 dw = 0x4C002000
          blockvga <= avs_s0_writedata[0];
        end
        2: begin // 0x1000 dw
          vga_base_addr <= avs_s0_writedata[15:0];
        end
        3: begin // 0x1800 dw
          write_cursor_ena <= 1;
        end
        4: begin // 0x2000 dw
          cursor_posX <= avs_s0_writedata[9:0];
        end
        5: begin // 0x2800 dw
          cursor_posY <= avs_s0_writedata[9:0];
        end
        6: begin // 0x3000 dw
          memcpy_startx <= avs_s0_writedata[9:0];//0~1024px
        end
        7: begin // 0x3800 dw
          memcpy_starty <= avs_s0_writedata[15:0];//0~16384line
        end
        8: begin // 0x4000 dw
          memcpy_width <= avs_s0_writedata[9:0];//max512
        end
        9: begin // 0x4800 dw
          memcpy_height <= avs_s0_writedata[15:0];//0~16384line
        end
        10:begin // 0x5000 dw
          memset_val <= avs_s0_writedata;
        end
        11:begin // 0x5800 dw
          memcpy_req <= avs_s0_writedata[0];
        end
        12:begin // 0x6000 dw
          memset_req <= avs_s0_writedata[0];
        end
        13:begin // 0x6800 dw
          memcpy_destx <= avs_s0_writedata[9:0];
        end
        14:begin // 0x7000 dw
          memcpy_desty <= avs_s0_writedata[15:0];
        end
        15:begin // 0x7800 dw
          memset_pattern <= avs_s0_writedata;
        end
        endcase
      end

      if(!device_req_buff && device_ack)begin
        device_ack <= 0;
      end

    end
  end
  
  
  //128m byte range 64k line * 2048byte per line
  assign read_cursor_data = {q_cursor[7:5],2'b0,q_cursor[4:2],3'b0,q_cursor[1:0],3'b0};//332->565
  wire [7:0] q_cursor;
`ifdef isALTERA
  altsyncram	buffCursor (
    .address_a (write_cursor_addr),
    .clock0 (clk_50M),
    .data_a (write_cursor_data),
    .wren_a (write_cursor_ena),
    .address_b (read_cursor_addr),
    .clock1 (read_cursor_clk),
    .q_b (q_cursor),
    .aclr0 (1'b0),
    .aclr1 (1'b0),
    .addressstall_a (1'b0),
    .addressstall_b (1'b0),
    .byteena_a (1'b1),
    .byteena_b (1'b1),
    .clocken0 (1'b1),
    .clocken1 (1'b1),
    .clocken2 (1'b1),
    .clocken3 (1'b1),
    .data_b ({8{1'b1}}),
    .eccstatus (),
    .q_a (),
    .rden_a (1'b1),
    .rden_b (1'b1),
    .wren_b (1'b0));
  defparam
    buffCursor.address_aclr_b = "NONE",
    buffCursor.address_reg_b = "CLOCK1",
    buffCursor.clock_enable_input_a = "BYPASS",
    buffCursor.clock_enable_input_b = "BYPASS",
    buffCursor.clock_enable_output_b = "BYPASS",
    buffCursor.intended_device_family = "Cyclone IV E",
    buffCursor.lpm_type = "altsyncram",
    buffCursor.numwords_a = 1024,
    buffCursor.numwords_b = 1024,
    buffCursor.operation_mode = "DUAL_PORT",
    buffCursor.outdata_aclr_b = "NONE",
    buffCursor.outdata_reg_b = "UNREGISTERED",
    buffCursor.power_up_uninitialized = "FALSE",
    buffCursor.ram_block_type = "M9K",
    buffCursor.widthad_a = 10,
    buffCursor.widthad_b = 10,
    buffCursor.width_a = 8,
    buffCursor.width_b = 8,
    buffCursor.width_byteena_a = 1;
`endif
`ifdef isVIVADO
  ram8x1024 buffCursor (
    .clka  (clk_50M),
    .wea   (write_cursor_ena),
    .addra (write_cursor_addr),
    .dina  (write_cursor_data),
    .clkb  (read_cursor_clk),
    .addrb (read_cursor_addr),
    .doutb (q_cursor)
  );

`endif


  assign avs_s0_readdata = {memset_ack, memcpy_ack};

  assign dma_len = memcpy_width;//max512
  assign dma_start_addr = write_dma_req ? {memcpy_dest_line[14:0],memcpy_destx} : {memcpy_curr_line[14:0],memcpy_startx};//pixel(word) based addr total 16M

  wire [15:0] memcpy_endof_line = memcpy_desty + memcpy_height;
  reg memset_ack;
  reg memcpy_ack;
  reg [15:0] memcpy_curr_line;
  reg [15:0] memcpy_dest_line;
  reg [2:0] memcpy_status;
  reg [2:0] memset_status;
  reg       read_dma_ack_buff;
  reg       write_dma_ack_buff;
  always@(posedge clk_50M or negedge reset_n) begin
    if(!reset_n) begin
      read_dma_req <= 0;
      write_dma_req <= 0;
      memcpy_curr_line <= 0;
      memcpy_dest_line <= 0;
      read_dma_ack_buff <= 0;
      write_dma_ack_buff <= 0;
      memcpy_status <= 0;
      memset_status <= 0;
      memset_ack <= 0;
      memcpy_ack <= 0;
    end else begin
      read_dma_ack_buff <= read_dma_ack;
      write_dma_ack_buff <= write_dma_ack;

      case(memcpy_status)
      0:begin
        if(memcpy_req && !memcpy_ack)begin
          memcpy_curr_line <= memcpy_starty;
          memcpy_dest_line <= memcpy_desty;
          memcpy_status <= 1;
        end
      end
      1:begin
        if(memcpy_dest_line == memcpy_endof_line)begin
          memcpy_ack <= 1;
          memcpy_status <= 0;
        end else begin
          read_dma_req <= 1;
          memcpy_status <= 2;
        end
      end
      2:begin
        if(read_dma_ack_buff)begin
          read_dma_req <= 0;
          memcpy_status <= 3;
        end
      end
      3:begin
        if(!read_dma_ack_buff)begin
          write_dma_req <= 1;
          memcpy_status <= 4;
        end
      end
      4:begin
        if(write_dma_ack_buff)begin
          write_dma_req <= 0;
          memcpy_status <= 5;
        end
      end
      5:begin
        if(!write_dma_ack_buff)begin
          memcpy_curr_line <= memcpy_curr_line + 1'b1;
          memcpy_dest_line <= memcpy_dest_line + 1'b1;
          memcpy_status <= 1;
        end
      end

      endcase
    
      if(!memcpy_req && memcpy_ack)begin
        memcpy_ack <= 0;
      end

      case(memset_status)
      0:begin
        if(memset_req && !memset_ack)begin
          memcpy_dest_line <= memcpy_desty;
          memset_status <= 1;
        end
      end
      1:begin
        if(memcpy_dest_line == memcpy_endof_line)begin
          memset_ack <= 1;
          memset_status <= 0;
        end else begin
          write_dma_req <= 1;
          memset_status <= 4;
        end
      end
      4:begin
        if(write_dma_ack_buff)begin
          write_dma_req <= 0;
          memset_status <= 5;
        end
      end
      5:begin
        if(!write_dma_ack_buff)begin
          memcpy_dest_line <= memcpy_dest_line + 1'b1;
          memset_status <= 1;
        end
      end

      endcase
    
      if(!memset_req && memset_ack)begin
        memset_ack <= 0;
      end

    end
  end

  //8bitwidth:4line
  //16bitwidth:2line
  //putchar;
  
  wire putchar_flg = memcpy_width == 8;
  
  wire [1:0] putchar_yindex = memcpy_dest_line[1:0] - memcpy_desty[1:0];
  wire putchar_pattern8  = memset_pattern[{putchar_yindex[1:0],dma_sram_address[2:0]}];//8bit width
  wire putchar_pattern16 = memset_pattern[{putchar_yindex[  0],dma_sram_address[3:0]}];//16bit width
  wire putchar_pattern = putchar_flg ? putchar_pattern8 : putchar_pattern16;
  reg putchar_pattern_reg;
  always@(posedge dma_sram_clk) begin
    putchar_pattern_reg <= putchar_pattern;
  end
  wire [15:0] memset_feed = putchar_pattern_reg ? memset_val[31:16] : memset_val[15:0];

  assign dma_sram_rdata = memcpy_req ? q_memcpyBuff : memset_feed;
  wire [15:0] q_memcpyBuff;
`ifdef isALTERA
  altsyncram memcpyBuff (
    .clock0 (dma_sram_clk),
    .address_a (dma_sram_address),
    .data_a (dma_sram_wdata),
    .wren_a (dma_sram_write_enable),
    .byteena_a (2'b11),
    .q_a (q_memcpyBuff),
    .address_b (9'b0),
    .q_b (),
    //.data_b (1'b0),
    .wren_b (1'b0),
    .aclr0 (1'b0),
    .aclr1 (1'b0),
    .addressstall_a (1'b0),
    .addressstall_b (1'b0),
    //.byteena_b (1'b1),
    .clock1 (1'b1),
    .clocken0 (1'b1),
    .clocken1 (1'b1),
    .clocken2 (1'b1),
    .clocken3 (1'b1),
    .eccstatus (),
    .rden_a (1'b1),
    .rden_b (1'b1));
  defparam
    memcpyBuff.address_reg_b = "CLOCK0",
    memcpyBuff.clock_enable_input_a = "BYPASS",
    memcpyBuff.clock_enable_input_b = "BYPASS",
    memcpyBuff.clock_enable_output_a = "BYPASS",
    memcpyBuff.clock_enable_output_b = "BYPASS",
    memcpyBuff.indata_reg_b = "CLOCK0",
    memcpyBuff.intended_device_family = "Cyclone IV E",
    memcpyBuff.lpm_type = "altsyncram",
    memcpyBuff.numwords_a = 512,
    memcpyBuff.numwords_b = 512,
    memcpyBuff.operation_mode = "BIDIR_DUAL_PORT",
    memcpyBuff.outdata_aclr_a = "NONE",
    memcpyBuff.outdata_aclr_b = "NONE",
    memcpyBuff.outdata_reg_a = "UNREGISTERED",
    memcpyBuff.outdata_reg_b = "UNREGISTERED",
    memcpyBuff.power_up_uninitialized = "FALSE",
    memcpyBuff.ram_block_type = "M9K",
    memcpyBuff.read_during_write_mode_mixed_ports = "DONT_CARE",
    memcpyBuff.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ",
    memcpyBuff.read_during_write_mode_port_b = "NEW_DATA_NO_NBE_READ",
    memcpyBuff.widthad_a = 9,
    memcpyBuff.widthad_b = 9,
    memcpyBuff.width_a = 16,
    memcpyBuff.width_b = 16,
    memcpyBuff.width_byteena_a = 2,
    memcpyBuff.width_byteena_b = 2,
    memcpyBuff.wrcontrol_wraddress_reg_b = "CLOCK0";
`endif
`ifdef isVIVADO
  ram16x512 ram16x512_ins (
    .clka  (dma_sram_clk),
    .wea   (dma_sram_write_enable),
    .addra (dma_sram_address),
    .dina  (dma_sram_wdata),
    .douta (q_memcpyBuff)
  );
`endif


endmodule
